<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  
  
  <title>OAuth2 | Hexo</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <meta name="description" content="OAuth21.微服务的 OAuth21.为什么需要 OAuth2 第三方登录就是一个 OAuth2 的典型应用1.1快递员问题 我住在一个大型的居民小区。  | 项目    | 端口 | 备注   || —————- | ———— | ————— || auth-server | 8080     | 授权服务器 || user-server | 8081     | 资源服务器 || clie">
<meta property="og:type" content="article">
<meta property="og:title" content="OAuth2">
<meta property="og:url" content="https://goofyer.gitee.io/notes-on-computer-expertise/2022/02/20/Spring%E5%BC%80%E5%8F%91/OAuth2/index.html">
<meta property="og:site_name" content="Hexo">
<meta property="og:description" content="OAuth21.微服务的 OAuth21.为什么需要 OAuth2 第三方登录就是一个 OAuth2 的典型应用1.1快递员问题 我住在一个大型的居民小区。  | 项目    | 端口 | 备注   || —————- | ———— | ————— || auth-server | 8080     | 授权服务器 || user-server | 8081     | 资源服务器 || clie">
<meta property="og:locale" content="en_US">
<meta property="og:image" content="https://goofyer.gitee.io/notes-on-computer-expertise/2022/02/20/Spring%E5%BC%80%E5%8F%91/image-20220220152236170.png">
<meta property="article:published_time" content="2022-02-20T07:00:58.000Z">
<meta property="article:modified_time" content="2022-02-20T07:29:22.803Z">
<meta property="article:author" content="John Doe">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://goofyer.gitee.io/notes-on-computer-expertise/2022/02/20/Spring%E5%BC%80%E5%8F%91/image-20220220152236170.png">
  
    <link rel="alternate" href="/notes-on-computer-expertise/atom.xml" title="Hexo" type="application/atom+xml">
  
  
    <link rel="shortcut icon" href="/notes-on-computer-expertise/favicon.png">
  
  
    
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/typeface-source-code-pro@0.0.71/index.min.css">

  
  
<link rel="stylesheet" href="/notes-on-computer-expertise/css/style.css">

  
    
<link rel="stylesheet" href="/notes-on-computer-expertise/fancybox/jquery.fancybox.min.css">

  
<meta name="generator" content="Hexo 5.4.0"></head>

<body>
  <div id="container">
    <div id="wrap">
      <header id="header">
  <div id="banner"></div>
  <div id="header-outer" class="outer">
    <div id="header-title" class="inner">
      <h1 id="logo-wrap">
        <a href="/notes-on-computer-expertise/" id="logo">Hexo</a>
      </h1>
      
    </div>
    <div id="header-inner" class="inner">
      <nav id="main-nav">
        <a id="main-nav-toggle" class="nav-icon"></a>
        
          <a class="main-nav-link" href="/notes-on-computer-expertise/">Home</a>
        
          <a class="main-nav-link" href="/notes-on-computer-expertise/archives">Archives</a>
        
      </nav>
      <nav id="sub-nav">
        
          <a id="nav-rss-link" class="nav-icon" href="/notes-on-computer-expertise/atom.xml" title="RSS Feed"></a>
        
        <a id="nav-search-btn" class="nav-icon" title="Search"></a>
      </nav>
      <div id="search-form-wrap">
        <form action="//google.com/search" method="get" accept-charset="UTF-8" class="search-form"><input type="search" name="q" class="search-form-input" placeholder="Search"><button type="submit" class="search-form-submit">&#xF002;</button><input type="hidden" name="sitesearch" value="https://goofyer.gitee.io/notes-on-computer-expertise"></form>
      </div>
    </div>
  </div>
</header>

      <div class="outer">
        <section id="main"><article id="post-Spring开发/OAuth2" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/notes-on-computer-expertise/2022/02/20/Spring%E5%BC%80%E5%8F%91/OAuth2/" class="article-date">
  <time class="dt-published" datetime="2022-02-20T07:00:58.000Z" itemprop="datePublished">2022-02-20</time>
</a>
    
  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 class="p-name article-title" itemprop="headline name">
      OAuth2
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h1 id="OAuth2"><a href="#OAuth2" class="headerlink" title="OAuth2"></a><strong>OAuth2</strong></h1><h2 id="1-微服务的-OAuth2"><a href="#1-微服务的-OAuth2" class="headerlink" title="1.微服务的 OAuth2"></a>1.微服务的 OAuth2</h2><h3 id="1-为什么需要-OAuth2"><a href="#1-为什么需要-OAuth2" class="headerlink" title="1.为什么需要 OAuth2"></a>1.为什么需要 OAuth2</h3><ul>
<li>第三方登录就是一个 OAuth2 的典型应用<h4 id="1-1快递员问题"><a href="#1-1快递员问题" class="headerlink" title="1.1快递员问题"></a>1.1快递员问题</h4></li>
<li><p>我住在一个大型的居民小区。</p>
</li>
<li><p>| <strong>项目</strong>    | <strong>端口</strong> | <strong>备注</strong>   |<br>| —————- | ———— | ————— |<br>| auth-server | 8080     | 授权服务器 |<br>| user-server | 8081     | 资源服务器 |<br>| client-app  | 8082     | 第三方应用|</p>
</li>
<li><p>就是说，我们常见的 OAuth2 授权码模式登录中，涉及到的各个角色，我都会自己提供，自己测试，这样可以最大限度的让小伙伴们了解到 OAuth2 的工作原理（文末可以下载案例源码）。</p>
</li>
<li>注意：小伙伴们一定先看下上篇文章所讲的 OAuth2 授权码模式登录流程，再来学习本文。<br>那我们首先来创建一个空的 Maven 父工程，创建好之后，里边什么都不用加，也不用写代码。我们将在这个父工程中搭建这个子模块。<h4 id="1-2授权机制的设计"><a href="#1-2授权机制的设计" class="headerlink" title="1.2授权机制的设计"></a>1.2授权机制的设计</h4></li>
<li><p>首先我们搭建一个名为 auth-server 的授权服务，搭建的时候，选择如下三个依赖：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">web</span><br><span class="line">spring cloud security </span><br><span class="line">spirng cloud OAuth2</span><br></pre></td></tr></table></figure>
</li>
<li><p>项目创建完成后，首先提供一个 Spring Security 的基本配置</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">@Configuration</span><br><span class="line">public class SecurityConfig extends WebSecurityConfigurerAdapter</span><br><span class="line">&#123; @Bean</span><br><span class="line">PasswordEncoder passwordEncoder()</span><br><span class="line">&#123; return new</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">@Override</span><br><span class="line">protected void configure(AuthenticationManagerBuilder auth) throws Exception</span><br><span class="line">&#123;</span><br><span class="line">auth.inMemoryAuthentication()</span><br><span class="line">.withUser(&quot;sang&quot;)</span><br><span class="line">.password(new BCryptPasswordEncoder().encode(&quot;123&quot;))</span><br><span class="line">.roles(&quot;admin&quot;)</span><br><span class="line">.and()</span><br><span class="line">.withUser(&quot;javaboy&quot;)</span><br><span class="line">.password(new BCryptPasswordEncoder().encode(&quot;123&quot;))</span><br><span class="line">.roles(&quot;user&quot;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">@Override</span><br><span class="line">protected void configure(HttpSecurity http) throws Exception</span><br><span class="line">&#123; http.csrf().disable().formLogin();</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line">- 在这段代码中，为了代码简洁，我就不把 Spring Security 用户存到数据库中去了，直接存在内存中。这里我创建了一个名为 sang 的用户，密码是 123，角色是 admin。同时我还配置了一个表单登录。</span><br><span class="line">- 这段配置的目的，实际上就是配置用户。例如你想用微信登录第三方网站，在这个过程中，你得先登录微信，登录微信就要你的用户名/密码信息，那么我们在这里配置的，其实就是用户的用户名/密码/角色信息。</span><br><span class="line">- 基本的用户信息配置完成后，接下来我们来配置授权服务器：</span><br><span class="line">@Configuration</span><br><span class="line">public class AccessTokenConfig</span><br><span class="line">&#123; @Bean</span><br><span class="line">TokenStore tokenStore() &#123;</span><br><span class="line">return new InMemoryTokenStore();</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">@EnableAuthorizationServer @Configuration</span><br><span class="line">public class AuthorizationServer extends AuthorizationServerConfigurerAdapter</span><br><span class="line">&#123; @Autowired TokenStore tokenStore; @Autowired</span><br><span class="line">ClientDetailsService clientDetailsService;</span><br><span class="line"></span><br><span class="line">@Bean</span><br><span class="line">AuthorizationServerTokenServices tokenServices()</span><br><span class="line">&#123; DefaultTokenServices services = new DefaultTokenServices(); services.setClientDetailsService(clientDetailsService); services.setSupportRefreshToken(true); services.setTokenStore(tokenStore); services.setAccessTokenValiditySeconds(60 * 60 * 2);</span><br><span class="line">services.setRefreshTokenValiditySeconds(60 * 60 * 24 * 3); return services;</span><br><span class="line">&#125;</span><br><span class="line">public void configure(AuthorizationServerSecurityConfigurer security) throws Exception &#123;</span><br><span class="line">security.checkTokenAccess(&quot;permitAll()&quot;)</span><br><span class="line">.allowFormAuthenticationForClients();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">@Override</span><br><span class="line">public void configure(ClientDetailsServiceConfigurer clients) throws Exception &#123;</span><br><span class="line">clients.inMemory()</span><br><span class="line">.withClient(&quot;javaboy&quot;)</span><br><span class="line">.secret(new BCryptPasswordEncoder().encode(&quot;123&quot;))</span><br><span class="line">.resourceIds(&quot;res1&quot;)</span><br><span class="line">.authorizedGrantTypes(&quot;authorization_code&quot;,&quot;refresh_token&quot;)</span><br><span class="line">.scopes(&quot;all&quot;)</span><br><span class="line">.redirectUris(&quot;http://localhost:8082/index.html&quot;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">@Override</span><br><span class="line">public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception &#123;</span><br><span class="line">endpoints.authorizationCodeServices(authorizationCodeServices())</span><br><span class="line">.tokenServices(tokenServices());</span><br><span class="line">&#125;</span><br><span class="line">@Bean</span><br><span class="line">AuthorizationCodeServices authorizationCodeServices()</span><br><span class="line">&#123; return new InMemoryAuthorizationCodeServices();</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>1.首先我们提供了一个 TokenStore 的实例，这个是指你生成的 Token 要往哪里存储，我们可以存在 Redis 中，也可以存在内存中，也可以结合 JWT 等等，这里，我们就先把它存在内存中，所以提供一个 InMemoryTokenStore 的实例即可。</p>
</li>
<li>2.接下来我们创建 AuthorizationServer 类继承自 AuthorizationServerConﬁgurerAdapter，来对授权服务器做进一步的详细配置，AuthorizationServer 类记得加上@EnableAuthorizationServer 注解，表示开启授权服务器的自动化配置。</li>
<li>3.在 AuthorizationServer 类中，我们其实主要重写三个 conﬁgure 方法。</li>
<li>4.AuthorizationServerSecurityConﬁgurer 用来配置令牌端点的安全约束，也就是这个端点谁能访问，谁不能访问。checkTokenAccess 是指一个 Token 校验的端点，这个端点我们设置为可以直接访问（在后面，当资源服务器收到 Token 之后，需要去校验 Token 的合法性，就会访问这个端点）。</li>
<li>5.ClientDetailsServiceConﬁgurer 用来配置客户端的详细信息，在上篇文章中，和大家讲过，   授权服务器要做两方面的检验，一方面是校验客户端，另一方面则是校验用户，校验用户，我们前 面已经配置了，这里就是配置校验客户端。客户端的信息我们可以存在数据库中，这其实也是比较 容易的，和用户信息存到数据库中类似，但是这里为了简化代码，我还是将客户端信息存在内存    中 ， 这里我们分别配置了客户端的 id，secret、资源 id、授权类型、授权范围以及重定向 uri。授权类型我在上篇文章中和大家一共讲了四种，四种之中不包含 refresh_token 这种类型，但是在实际操作中，refresh_token 也被算作一种。</li>
<li>6.AuthorizationServerEndpointsConﬁgurer 这里用来配置令牌的访问端点和令牌服务。authorizationCodeServices用来配置授权码的存储，这里我们是存在在内存中，tokenServices    用来配置令牌的存储，即 access_token 的存储位置，这里我们也先存储在内存中。有小伙伴会问，授权码和令牌有什么区别？授权码是用来获取令牌的，使用一次就失效，令牌则是用来获取资源的，如果搞不清楚，建议重新阅读上篇文章恶补一下：做微服务绕不过的 OAuth2，也来和大家扯一扯</li>
<li>7.tokenServices 这个 Bean 主要用来配置 Token 的一些基本信息，例如 Token 是否支持刷新、Token 的存储位置、Token 的有效期以及刷新 Token 的有效期等等。Token 有效期这个好理解， 刷新 Token 的有效期我说一下，当 Token 快要过期的时候，我们需要获取一个新的 Token，在获取新的 Token 时候，需要有一个凭证信息，这个凭证信息不是旧的 Token，而是另外一个refresh_token，这个 refresh_token 也是有有效期的。</li>
<li>3.资源服务器搭建</li>
<li><p>接下来我们搭建一个资源服务器。大家网上看到的例子，资源服务器大多都是和授权服务器放在一起     的，如果项目比较小的话，这样做是没问题的，但是如果是一个大项目，这种做法就不合适了。资源服务器就是用来存放用户的资源，例如你在微信上的图像、openid 等信息，用户从授权服务器上拿到 access_token 之后，接下来就可以通过 access_token 来资源服务器请求数据。我们创建一个新的 Spring Boot 项目，叫做 user-server ，作为我们的资源服务器，创建时，添加如下依赖：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">  @Configuration @EnableResourceServer</span><br><span class="line">  public class ResourceServerConfig extends ResourceServerConfigurerAdapter</span><br><span class="line">  &#123; @Bean</span><br><span class="line">  RemoteTokenServices tokenServices() &#123;</span><br><span class="line">  RemoteTokenServices services = new RemoteTokenServices();</span><br><span class="line"></span><br><span class="line">services.setCheckTokenEndpointUrl(&quot;http://localhost:8080/oauth/check_token&quot;); services.setClientId(&quot;javaboy&quot;);</span><br><span class="line">services.setClientSecret(&quot;123&quot;); return services;</span><br><span class="line">&#125;</span><br><span class="line">@Override</span><br><span class="line">public void configure(ResourceServerSecurityConfigurer resources) throws Exception &#123;</span><br><span class="line">resources.resourceId(&quot;res1&quot;).tokenServices(tokenServices());</span><br><span class="line">&#125;</span><br><span class="line">@Override</span><br><span class="line">public void configure(HttpSecurity http) throws Exception</span><br><span class="line">&#123; http.authorizeRequests()</span><br><span class="line">.antMatchers(&quot;/admin/**&quot;).hasRole(&quot;admin&quot;)</span><br><span class="line">.anyRequest().authenticated();</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><ul>
<li>1.tokenServices 我们配置了一个 RemoteTokenServices  的实例，这是因为资源服务器和授权服务器是分开的，资源服务器和授权服务器是放在一起的，就不需要配置 RemoteTokenServices 了。</li>
<li>2.RemoteTokenServices 中我们配置了 access_token 的校验地址、client_id、client_secret 这三个信息，当用户来资源服务器请求资源时，会携带上一个   access_token，通过这里的配置，就能够校验出 token 是否正确等。最后配置一下资源的拦截规则，这就是 Spring Security 中的基本写法，我就不再赘述。</li>
</ul>
</li>
<li><p>4.第三方应用搭建</p>
<ul>
<li><p><img src="../image-20220220152236170.png" alt="image-20220220152236170"></p>
</li>
<li><p>在 resources/templates 目录下，创建 index.html ，内容如下</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">&lt;!DOCTYPE html&gt;</span><br><span class="line">&lt;html lang=&quot;en&quot; xmlns:th=&quot;http://www.thymeleaf.org&quot;&gt;</span><br><span class="line">&lt;head&gt;</span><br><span class="line">&lt;meta charset=&quot;UTF-8&quot;&gt;</span><br><span class="line">&lt;title&gt;北京&lt;/title&gt;</span><br><span class="line">&lt;/head&gt;</span><br><span class="line">&lt;body&gt;</span><br><span class="line">你好，北京！ </span><br><span class="line">&lt;a href=&quot;http://localhost:8080/oauth/authorize? client_id=javaboy&amp;response_type=code&amp;scope=all&amp;redirect_uri=http://localhost:808 2/index.html&quot;&gt;第三方登录&lt;/a&gt;	</span><br><span class="line"></span><br><span class="line">&lt;h1 th:text=&quot;$&#123;msg&#125;&quot;&gt;&lt;/h1&gt;	</span><br><span class="line">&lt;/body&gt;</span><br><span class="line">&lt;/html&gt;</span><br></pre></td></tr></table></figure>
</li>
</ul>
</li>
<li><ul>
<li>这是一段 Thymeleaf 模版，点击超链接就可以实现第三方登录，超链接的参数如下：<ul>
<li>client_id 客户端 ID，根据我们在授权服务器中的实际配置填写。response_type 表示响应类型，这里是 code 表示响应一个授权码。redirect_uri 表示授权成功后的重定向地址，这里表示回到第三方应用的首页。scope 表示授权范围。</li>
<li>h1 标签中的数据是来自资源服务器的，当授权服务器通过后，我们拿着 access_token 去资源服务器加载数据，加载到的数据就在 h1 标签中显示出来。</li>
<li>接下来我们来定义一个 HelloController：<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">@Controller</span><br><span class="line">public class HelloController</span><br><span class="line">&#123; @Autowired</span><br><span class="line">RestTemplate restTemplate;</span><br><span class="line">@GetMapping(&quot;/index.html&quot;)</span><br><span class="line">public String hello(String code, Model model)</span><br><span class="line">&#123; if (code != null) &#123;</span><br><span class="line">MultiValueMap&lt;String, String&gt; map = new LinkedMultiValueMap&lt;&gt;(); map.add(&quot;code&quot;, code);</span><br><span class="line">map.add(&quot;client_id&quot;, &quot;javaboy&quot;); map.add(&quot;client_secret&quot;, &quot;123&quot;);</span><br><span class="line">map.add(&quot;redirect_uri&quot;, &quot;http://localhost:8082/index.html&quot;); map.add(&quot;grant_type&quot;, &quot;authorization_code&quot;); Map&lt;String,String&gt; resp =</span><br><span class="line">restTemplate.postForObject(&quot;http://localhost:8080/oauth/token&quot;, map, Map.class); String access_token = resp.get(&quot;access_token&quot;); System.out.println(access_token);</span><br><span class="line">HttpHeaders headers = new HttpHeaders(); headers.add(&quot;Authorization&quot;, &quot;Bearer &quot; + access_token); HttpEntity&lt;Object&gt; httpEntity = new HttpEntity&lt;&gt;(headers); ResponseEntity&lt;String&gt; entity =</span><br><span class="line">restTemplate.exchange(&quot;http://localhost:8081/admin/hello&quot;, HttpMethod.GET, httpEntity, String.class);</span><br><span class="line">model.addAttribute(&quot;msg&quot;, entity.getBody());</span><br><span class="line">&#125;</span><br><span class="line">return &quot;index&quot;;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h4 id="1-3互联网场景"><a href="#1-3互联网场景" class="headerlink" title="1.3互联网场景"></a>1.3互联网场景</h4><h4 id="1-4令牌与密码"><a href="#1-4令牌与密码" class="headerlink" title="1.4令牌与密码"></a>1.4令牌与密码</h4><h3 id="2-什-么是-OAuth2"><a href="#2-什-么是-OAuth2" class="headerlink" title="2.什 么是 OAuth2"></a>2.什 么是 OAuth2</h3><h3 id="3-四种模式"><a href="#3-四种模式" class="headerlink" title="3.四种模式"></a>3.四种模式</h3><h4 id="3-1授权码模式"><a href="#3-1授权码模式" class="headerlink" title="3.1授权码模式"></a>3.1授权码模式</h4><h4 id="3-2简化模式"><a href="#3-2简化模式" class="headerlink" title="3.2简化模式"></a>3.2简化模式</h4><h4 id="3-3密码模式"><a href="#3-3密码模式" class="headerlink" title="3.3密码模式"></a>3.3密码模式</h4><h4 id="3-4客户端模式"><a href="#3-4客户端模式" class="headerlink" title="3.4客户端模式"></a>3.4客户端模式</h4><h3 id="4-小结"><a href="#4-小结" class="headerlink" title="4.小结"></a>4.小结</h3><h2 id="2-这个案例写出来，明白-OAuth2-登录流程"><a href="#2-这个案例写出来，明白-OAuth2-登录流程" class="headerlink" title="2.这个案例写出来，明白 OAuth2 登录流程"></a>2.这个案例写出来，明白 OAuth2 登录流程</h2><h3 id="1-案例架构"><a href="#1-案例架构" class="headerlink" title="1.案例架构"></a>1.案例架构</h3><h3 id="2-授权服务器搭建"><a href="#2-授权服务器搭建" class="headerlink" title="2.授权服务器搭建"></a>2.授权服务器搭建</h3><h3 id="3-资源服务器搭建"><a href="#3-资源服务器搭建" class="headerlink" title="3.资源服务器搭建"></a>3.资源服务器搭建</h3><h3 id="4-第三方应用搭建"><a href="#4-第三方应用搭建" class="headerlink" title="4.第三方应用搭建"></a>4.第三方应用搭建</h3><h3 id="5-测试"><a href="#5-测试" class="headerlink" title="5.测试"></a>5.测试</h3><h2 id="3-OAuth2详细"><a href="#3-OAuth2详细" class="headerlink" title="3.OAuth2详细"></a>3.OAuth2详细</h2><h3 id="1-简化模式"><a href="#1-简化模式" class="headerlink" title="1.简化模式"></a>1.简化模式</h3><h3 id="2-密码模式"><a href="#2-密码模式" class="headerlink" title="2.密码模式"></a>2.密码模式</h3><h3 id="3-客户端模式"><a href="#3-客户端模式" class="headerlink" title="3.客户端模式"></a>3.客户端模式</h3><h3 id="4-刷-新-token"><a href="#4-刷-新-token" class="headerlink" title="4.刷 新 token"></a>4.刷 新 token</h3><h2 id="4-其他"><a href="#4-其他" class="headerlink" title="4.其他"></a>4.其他</h2><h2 id="4-OAuth2-令牌还能存入-Redis"><a href="#4-OAuth2-令牌还能存入-Redis" class="headerlink" title="4.OAuth2 令牌还能存入 Redis"></a>4.OAuth2 令牌还能存入 Redis</h2><h3 id="1-令牌往哪里存？"><a href="#1-令牌往哪里存？" class="headerlink" title="1.令牌往哪里存？"></a>1.令牌往哪里存？</h3><h3 id="2-客户端信息入库"><a href="#2-客户端信息入库" class="headerlink" title="2.客户端信息入库"></a>2.客户端信息入库</h3><h3 id="3-第三方应用优化"><a href="#3-第三方应用优化" class="headerlink" title="3.第三方应用优化"></a>3.第三方应用优化</h3><h2 id="5-想让-OAuth2-和-JWT"><a href="#5-想让-OAuth2-和-JWT" class="headerlink" title="5.想让 OAuth2 和 JWT"></a>5.想让 OAuth2 和 JWT</h2><h3 id="1-无状态登录"><a href="#1-无状态登录" class="headerlink" title="1.无状态登录"></a>1.无状态登录</h3><h4 id="1-1什么是有状态"><a href="#1-1什么是有状态" class="headerlink" title="1.1什么是有状态"></a>1.1什么是有状态</h4><h4 id="1-2什么是无状态"><a href="#1-2什么是无状态" class="headerlink" title="1.2什么是无状态"></a>1.2什么是无状态</h4><h4 id="1-3如何实现无状态"><a href="#1-3如何实现无状态" class="headerlink" title="1.3如何实现无状态"></a>1.3如何实现无状态</h4><h4 id="1-4JWT"><a href="#1-4JWT" class="headerlink" title="1.4JWT"></a>1.4JWT</h4><h5 id="1-4-1简介"><a href="#1-4-1简介" class="headerlink" title="1.4.1简介"></a>1.4.1简介</h5><h5 id="1-4-2JWT-数据格式"><a href="#1-4-2JWT-数据格式" class="headerlink" title="1.4.2JWT 数据格式"></a>1.4.2JWT 数据格式</h5><h5 id="1-4-3JWT-交互流程"><a href="#1-4-3JWT-交互流程" class="headerlink" title="1.4.3JWT 交互流程"></a>1.4.3JWT 交互流程</h5><h4 id="1-5JWT-存在的问题"><a href="#1-5JWT-存在的问题" class="headerlink" title="1.5JWT 存在的问题"></a>1.5JWT 存在的问题</h4><h3 id="2-OAuth2-中的问题"><a href="#2-OAuth2-中的问题" class="headerlink" title="2.OAuth2 中的问题"></a>2.OAuth2 中的问题</h3><h3 id="3-改造方案"><a href="#3-改造方案" class="headerlink" title="3.改造方案"></a>3.改造方案</h3><h4 id="3-1授权服务器改造"><a href="#3-1授权服务器改造" class="headerlink" title="3.1授权服务器改造"></a>3.1授权服务器改造</h4><h4 id="3-2资源服务器改造"><a href="#3-2资源服务器改造" class="headerlink" title="3.2资源服务器改造"></a>3.2资源服务器改造</h4><h3 id="4-测试"><a href="#4-测试" class="headerlink" title="4.测试"></a>4.测试</h3><h3 id="5-原理"><a href="#5-原理" class="headerlink" title="5.原理"></a>5.原理</h3><h2 id="6-Spring-Cloud-项目微服务架构中的安全管理"><a href="#6-Spring-Cloud-项目微服务架构中的安全管理" class="headerlink" title="6.Spring Cloud 项目微服务架构中的安全管理"></a>6.Spring Cloud 项目微服务架构中的安全管理</h2><h3 id="1-微服务架构"><a href="#1-微服务架构" class="headerlink" title="1.微服务架构"></a>1.微服务架构</h3><h3 id="2-为什么不建议-Cookie"><a href="#2-为什么不建议-Cookie" class="headerlink" title="2.为什么不建议 Cookie"></a>2.为什么不建议 Cookie</h3><h3 id="3-内部调用鉴权"><a href="#3-内部调用鉴权" class="headerlink" title="3.内部调用鉴权"></a>3.内部调用鉴权</h3><h3 id="4-还要不要-Spring-Security"><a href="#4-还要不要-Spring-Security" class="headerlink" title="4.还要不要 Spring Security"></a>4.还要不要 Spring Security</h3><h2 id="7-Spring-Boot-OAuth2，一个注解弄好单点登录！"><a href="#7-Spring-Boot-OAuth2，一个注解弄好单点登录！" class="headerlink" title="7.Spring Boot+OAuth2，一个注解弄好单点登录！"></a>7.Spring Boot+OAuth2，一个注解弄好单点登录！</h2><h3 id="1-项目创建"><a href="#1-项目创建" class="headerlink" title="1.项目创建"></a>1.项目创建</h3><h3 id="2-统一认证中心"><a href="#2-统一认证中心" class="headerlink" title="2.统一认证中心"></a>2.统一认证中心</h3><h3 id="3-客户端创建"><a href="#3-客户端创建" class="headerlink" title="3.客户端创建"></a>3.客户端创建</h3><h3 id="4-测试-1"><a href="#4-测试-1" class="headerlink" title="4.测试"></a>4.测试</h3><h3 id="5-流程解析"><a href="#5-流程解析" class="headerlink" title="5.流程解析"></a>5.流程解析</h3><h2 id="8-分分钟让自己的网站接入-GitHub-第三方登录功能"><a href="#8-分分钟让自己的网站接入-GitHub-第三方登录功能" class="headerlink" title="8.分分钟让自己的网站接入 GitHub 第三方登录功能"></a>8.分分钟让自己的网站接入 GitHub 第三方登录功能</h2><h3 id="1-准备工作"><a href="#1-准备工作" class="headerlink" title="1.准备工作"></a>1.准备工作</h3><h3 id="2-创建应用"><a href="#2-创建应用" class="headerlink" title="2.创建应用"></a>2.创建应用</h3><h2 id="9-Spring-Boot-OAuth2，如何自定义返回的-Token-信息？"><a href="#9-Spring-Boot-OAuth2，如何自定义返回的-Token-信息？" class="headerlink" title="9.Spring Boot+OAuth2，如何自定义返回的 Token 信息？"></a>9.Spring Boot+OAuth2，如何自定义返回的 Token 信息？</h2><h3 id="1-access-token-从哪里来"><a href="#1-access-token-从哪里来" class="headerlink" title="1.access_token 从哪里来"></a>1.access_token 从哪里来</h3><h3 id="2-两种定制方案"><a href="#2-两种定制方案" class="headerlink" title="2.两种定制方案"></a>2.两种定制方案</h3><h3 id="3-测试"><a href="#3-测试" class="headerlink" title="3.测试"></a>3.测试</h3><h3 id="4-扩展"><a href="#4-扩展" class="headerlink" title="4.扩展"></a>4.扩展</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"></span><br></pre></td></tr></table></figure>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"></span><br></pre></td></tr></table></figure>
      
    </div>
    <footer class="article-footer">
      <a data-url="https://goofyer.gitee.io/notes-on-computer-expertise/2022/02/20/Spring%E5%BC%80%E5%8F%91/OAuth2/" data-id="cl403sxqp000lf8vufchx7t1c" data-title="OAuth2" class="article-share-link">Share</a>
      
      
      
    </footer>
  </div>
  
    
<nav id="article-nav">
  
    <a href="/notes-on-computer-expertise/2022/02/20/%E6%95%B0%E6%8D%AE%E5%BA%93/%E7%AC%AC25%E7%AF%87%EF%BC%9Asql%E4%B8%AD%E7%9A%84where%E6%9D%A1%E4%BB%B6%E5%9C%A8%E6%95%B0%E6%8D%AE%E5%BA%93%E4%B8%AD%E6%8F%90%E5%8F%96%E4%B8%8E%E5%BA%94%E2%BD%A4%E6%B5%85%E6%9E%90/" id="article-nav-newer" class="article-nav-link-wrap">
      <strong class="article-nav-caption">Newer</strong>
      <div class="article-nav-title">
        
          第25篇：sql中的where条件在数据库中提取与应⽤浅析
        
      </div>
    </a>
  
  
    <a href="/notes-on-computer-expertise/2022/02/19/Spring%E5%BC%80%E5%8F%91/%E7%AC%AC%E4%B8%89%E7%AB%A0Spring-Cloud/" id="article-nav-older" class="article-nav-link-wrap">
      <strong class="article-nav-caption">Older</strong>
      <div class="article-nav-title">第三章Spring Cloud</div>
    </a>
  
</nav>

  
</article>


</section>
        
          <aside id="sidebar">
  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Categories</h3>
    <div class="widget">
      <ul class="category-list"><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/C-%E5%B7%A5%E5%85%B7/">C#工具</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/C-%E5%B7%A5%E5%85%B7/">C++工具</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/Mysql/">Mysql</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/blender/">blender</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/linux/">linux</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/manim/">manim</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/spring/">spring</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/vba%E5%9F%BA%E7%A1%80%E6%93%8D%E4%BD%9C/">vba基础操作</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E5%88%9B%E9%80%A0%E6%A8%A1%E5%BC%8F/">创造模式</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/">操作系统</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/">操作系统基础知识</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E5%BC%80%E5%8F%91/">操作系统开发</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E6%95%B0%E5%AD%A6/">数学</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/">数据结构</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/">数据结构基础知识</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E6%A8%A1%E6%9D%BF/">模板</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/">深度学习</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E8%8B%B1%E8%AF%AD/">英语</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E8%99%9A%E5%B9%BB4/">虚幻4</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BB%84%E6%88%90%E5%8E%9F%E7%90%86/">计算机组成原理</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/">计算机网络</a></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Tags</h3>
    <div class="widget">
      <ul class="tag-list" itemprop="keywords"><li class="tag-list-item"><a class="tag-list-link" href="/notes-on-computer-expertise/tags/C/" rel="tag">C++</a></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Tag Cloud</h3>
    <div class="widget tagcloud">
      <a href="/notes-on-computer-expertise/tags/C/" style="font-size: 10px;">C++</a>
    </div>
  </div>

  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Archives</h3>
    <div class="widget">
      <ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/notes-on-computer-expertise/archives/2022/05/">May 2022</a></li><li class="archive-list-item"><a class="archive-list-link" href="/notes-on-computer-expertise/archives/2022/03/">March 2022</a></li><li class="archive-list-item"><a class="archive-list-link" href="/notes-on-computer-expertise/archives/2022/02/">February 2022</a></li><li class="archive-list-item"><a class="archive-list-link" href="/notes-on-computer-expertise/archives/2022/01/">January 2022</a></li><li class="archive-list-item"><a class="archive-list-link" href="/notes-on-computer-expertise/archives/2021/12/">December 2021</a></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Recent Posts</h3>
    <div class="widget">
      <ul>
        
          <li>
            <a href="/notes-on-computer-expertise/2022/05/31/vue/%E5%89%8D%E7%AB%AF%E6%A1%86%E6%9E%B6%E9%AA%8C%E8%AF%81%E7%A0%81%E6%97%A0%E6%B3%95%E6%98%BE%E7%A4%BA/">(no title)</a>
          </li>
        
          <li>
            <a href="/notes-on-computer-expertise/2022/05/26/%E6%95%B0%E6%8D%AE%E5%BA%93/navicat%E5%AE%89%E8%A3%85/">(no title)</a>
          </li>
        
          <li>
            <a href="/notes-on-computer-expertise/2022/05/25/%E8%99%9A%E5%B9%BB4%E5%BC%95%E6%93%8E%E5%BC%80%E5%8F%91/%E8%99%9A%E5%B9%BB%E5%9B%9B%20mod%E5%88%B6%E4%BD%9C/">(no title)</a>
          </li>
        
          <li>
            <a href="/notes-on-computer-expertise/2022/05/23/python/python%E6%89%B9%E9%87%8F%E7%94%9F%E6%88%90%E6%95%B0%E6%8D%AE/">(no title)</a>
          </li>
        
          <li>
            <a href="/notes-on-computer-expertise/2022/05/23/vba%E5%9F%BA%E7%A1%80%E6%93%8D%E4%BD%9C%E7%AC%94%E8%AE%B0/EXCEL%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/">(no title)</a>
          </li>
        
      </ul>
    </div>
  </div>

  
</aside>
        
      </div>
      <footer id="footer">
  
  <div class="outer">
    <div id="footer-info" class="inner">
      
      &copy; 2022 John Doe<br>
      Powered by <a href="https://hexo.io/" target="_blank">Hexo</a>
    </div>
  </div>
</footer>

    </div>
    <nav id="mobile-nav">
  
    <a href="/notes-on-computer-expertise/" class="mobile-nav-link">Home</a>
  
    <a href="/notes-on-computer-expertise/archives" class="mobile-nav-link">Archives</a>
  
</nav>
    


<script src="/notes-on-computer-expertise/js/jquery-3.4.1.min.js"></script>



  
<script src="/notes-on-computer-expertise/fancybox/jquery.fancybox.min.js"></script>




<script src="/notes-on-computer-expertise/js/script.js"></script>





  </div>
<script type="text/x-mathjax-config">
    MathJax.Hub.Config({
        tex2jax: {
            inlineMath: [ ["$","$"], ["\\(","\\)"] ],
            skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code'],
            processEscapes: true
        }
    });
    MathJax.Hub.Queue(function() {
        var all = MathJax.Hub.getAllJax();
        for (var i = 0; i < all.length; ++i)
            all[i].SourceElement().parentNode.className += ' has-jax';
    });
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-MML-AM_CHTML"></script>
</body>
</html>